#pragma once

#include "infra/Rc.hpp"

#include <functional>
#include <set>

namespace rewine
{

class sync_value_t {
public:
    bool    valid;
    void *  address;

public:
    explicit sync_value_t(bool valid = false, void *address = nullptr)
        : valid(valid), address(address)
    {}

    friend bool operator ==(sync_value_t lhs, sync_value_t rhs) {
        if (!(lhs.valid && rhs.valid)) {
            return lhs.valid == rhs.valid;
        }
        return lhs.address == rhs.address;
    }
};

class ValueSynchronizer : public CRcObject {
public:

    using fnGetValue = sync_value_t();
    using fnSetValue = bool(sync_value_t);

    using fnOnFollowerEnter = void(ValueSynchronizer&);
    using fnOnFollowerLeave = void(ValueSynchronizer&);

    using fnOnRelease = void();

private:

    bool m_Closed{false};

    ValueSynchronizer * m_Leader{nullptr};
    bool m_Following{false};

    std::set<ValueSynchronizer *> m_Followers;

public:

    std::function<fnGetValue> getValueHandler;
    std::function<fnSetValue> setValueHandler;

    std::function<fnOnFollowerEnter> onFollowerEnterHandler;
    std::function<fnOnFollowerLeave> onFollowerLeaveHandler;

    std::function<fnOnRelease> onReleaseHandler;

private:

    void setLeader(ValueSynchronizer* leader) {
        if (m_Leader == leader) return;

        if (m_Leader) {
            onFollowerLeave(*this);
            m_Leader->FreeRef();
        }

        if (leader) {
            leader->TakeRef();
        }
        m_Leader = leader;
        m_Following = false;
        if (leader) {
            m_Leader->onFollowerEnter(*this);
        }
    }

    void onFollowerEnter(ValueSynchronizer& follower) {
        follower.TakeRef();
        if (onFollowerEnterHandler != nullptr) {
            onFollowerEnterHandler(follower);
        }
        m_Followers.insert(&follower);
    }

    void onFollowerLeave(ValueSynchronizer& follower) {
        if (!m_Closed) {
            m_Followers.erase(&follower);
        }
        if (onFollowerLeaveHandler != nullptr) {
            onFollowerLeaveHandler(follower);
        }
        follower.FreeRef();
    }

public:

    ValueSynchronizer() = default;

    ~ValueSynchronizer() {
        m_Closed = true;
        unfollow();

        auto&& followers(std::move(m_Followers));
        for (auto&& follower : followers) {
            follower->unfollow();
        }
    }

    virtual void OnReleased() override {
        // we destruct this object after its owner, rc == 0 means we should notify the owner, and the owner should handle it
        if (onReleaseHandler) {
            onReleaseHandler();
        }
    }

public:

    void follow(ValueSynchronizer& followed) {
        setLeader(&followed);
    }

    void unfollow() {
        setLeader(nullptr);
    }

    bool setValue(sync_value_t value, bool bDirect = true) {
        if (bDirect) {
            unfollow();
        }
        if (setValueHandler && setValueHandler(value)) {
            m_Following = true;
            for (auto pFollower : m_Followers) {
                pFollower->setValue(value, false);
            }
            return true;
        }
        return false;
    }

    sync_value_t getValue() {
        if (!m_Following) {
            if (m_Leader) {
                auto value = m_Leader->getValue();
                setValue(value, false);
            }
            // m_Leader == nullptr means this one is the top leader
        }
        if (!getValueHandler) {
            return sync_value_t();
        }
        return getValueHandler();
    }

};

} // namespace rewine
